/*
SerialHex - A tool for reading and writing serial port data
Copyright (C) 2007  Patrick J. McCarty

This file is part of SerialHex. SerialHex is free software;
you can redistribute it and/or modify it under the terms of the 
GNU General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Collections;

namespace TEAM.SerialTXRX
{
    public class SerialDeviceTanques
    {
        protected int[] baudRates;
        protected int defaultBaud;
        private int _dataBits;
        private Parity _Paridad;
        private StopBits _Stop;

        protected SerialPort serialPort = new SerialPort();

        public event InformationEventHandler ReportInfoT;
        public event ErrorEventHandler ReportErrorT;
        public event CommandEventHandler ReportCommandT;
        public event ReceiveDataEventHandler ReceiveDataT;

        private string hexPrefix = String.Empty; //gets changed to "0x" if user enables the prefix option
        private string delimiter = " "; //default is space, can be changed by user

        public SerialDeviceTanques(int[] baudRates, int defaultBaud)
        {
            if (Array.IndexOf(baudRates, defaultBaud) == -1)
            {
                throw new ArgumentException("defaultBaud must be listed in the array of supported baudRates");
            }
            this.baudRates = baudRates;
            this.defaultBaud = defaultBaud;
            this.serialPort.BaudRate = defaultBaud;
            // Configure serial as 8N1
            this.serialPort.DataBits = 7;
            this.serialPort.Parity = Parity.Odd;
            this.serialPort.StopBits = StopBits.One;
            //this.serialPort.ReadTimeout = 100; //***
            this.serialPort.NewLine = "\r\n";
            this.serialPort.Encoding = System.Text.Encoding.ASCII;
            this.serialPort.DataReceived += new SerialDataReceivedEventHandler(this.serialPort_DataReceived);
        }

        public SerialDeviceTanques(int[] baudRates, int defaultBaud, int DataBits, Parity Paridad, StopBits Stop)
        {
            if (Array.IndexOf(baudRates, defaultBaud) == -1)
            {
                throw new ArgumentException("defaultBaud must be listed in the array of supported baudRates");
            }
            this.baudRates = baudRates;
            this.defaultBaud = defaultBaud;
            this.serialPort.BaudRate = defaultBaud;
            // Configure serial as 8N1
            this.serialPort.DataBits = DataBits;
            this.serialPort.Parity = Paridad;
            this.serialPort.StopBits = Stop;
            //this.serialPort.ReadTimeout = 100; //***
            this.serialPort.NewLine = "\r\n";
            this.serialPort.Encoding = System.Text.Encoding.ASCII;
            this.serialPort.DataReceived += new SerialDataReceivedEventHandler(this.serialPort_DataReceived);
        }

        public int DataBits
        {
            set 
            { 
                _dataBits = value;
                serialPort.DataBits = _dataBits;
            }
        }

        public Parity Paridad
        {
            set 
            {
                _Paridad = value;
                serialPort.Parity = _Paridad;
            }
        }

        public StopBits Stop
        {
            set 
            {
                _Stop = value;
                serialPort.StopBits = _Stop;
            }
        }

        public string PortName
        {
            get
            {
                return this.serialPort.PortName;
            }
        }

        public virtual int BaudRate
        {
            get
            {
                return this.serialPort.BaudRate;
            }
            set
            {
                //if (Array.IndexOf(baudRates, value) == -1)
                //{
                //    throw new ArgumentException("New baud rate " + value + " is not listed in the array of supported baudRates");
                //}
                this.serialPort.BaudRate = value;
                if (ReportInfoT != null)
                {
                    ReportInfoT("Baud rate changed to: " + value);
                }
            }
        }



        public int[] BaudRates
        {
            get
            {
                return this.baudRates;
            }
            set
            {
                this.baudRates = value;
            }
        }

        public virtual bool Connect(string portName)
        {
            if (this.serialPort.IsOpen)
            {
                Disconnect();
                return false;
            }
            try
            {
                this.serialPort.PortName = portName;
                this.serialPort.Open();
                if (ReportInfoT != null)
                {
                    ReportInfoT("Connected to " + portName);
                }
                return true;
            }
            catch (Exception ex)
            {
                if (ReportErrorT != null)
                {
                    ReportErrorT("Connect(): " + ex.Message);
                }
                return false;
            }
        }

        public virtual bool Disconnect()
        {
            try
            {
                this.serialPort.Close();
                if (ReportInfoT != null)
                {
                    ReportInfoT("Disconnected");
                }
                return true;
            }
            catch (Exception ex)
            {
                if (ReportErrorT != null)
                {
                    ReportErrorT("Disconnect(): " + ex.Message);
                }
                return false;
            }
        }

        private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] buffer = new byte[serialPort.BytesToRead];
            serialPort.Read(buffer, 0, serialPort.BytesToRead);
            string HexData = string.Empty;
            string[] DataReceived = new string[] { };
            foreach (byte b in buffer)
            {
                HexData += ((char)b).ToString();
             }
            DataReceived = this.ManageDataReceivedTanques(HexData);
            if (ReceiveDataT != null)
            {
                ReceiveDataT(DataReceived);
            }
        }

        public byte[] DataReceivedFromSerialPort()
        {
            byte[] buffer = new byte[serialPort.BytesToRead];
            serialPort.Read(buffer, 0, serialPort.BytesToRead);
            return buffer;
        }

        public virtual void serialWrite(string text, string lineEnding)
        {
            if (ReportCommandT != null)
            {
                ReportCommandT(text + lineEnding.Replace("\r", @"\r").Replace("\n", @"\n"));
            }
            try
            {
                this.serialPort.Write(text + lineEnding);
            }
            catch (Exception ex)
            {
                if (ReportErrorT != null)
                {
                    ReportErrorT("serialWrite(): " + ex.Message);
                }
            }
        }

        public virtual void serialWrite(byte[] data)
        {
            try
            {
                this.serialPort.Write(data, 0, data.Length);
            }
            catch (Exception ex)
            {
                if (ReportErrorT != null)
                {
                    ReportErrorT("serialWrite(): " + ex.Message);
                }
            }
        }

        public void SendASCII(string TransmitASCII)
        {
            try
            {
                serialWrite(TransmitASCII, "");
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public void SendHex(string TransmitHex)
        {
            // strip the input of all delimiter characters and the optional 0x prefix
            string data = TransmitHex.Replace("0x", String.Empty).Replace(@"\n", "0A").Replace(@"\r", "0D").Replace(delimiter, String.Empty).Replace(",", String.Empty).Replace(" ", String.Empty).ToUpper();

            // check for invalid characters
            string invalidChars = String.Empty;
            for (int i = 0; i < data.Length; i++)
            {
                if (!Char.IsDigit(data[i]) && !(data[i] >= 'A' && data[i] <= 'F'))
                {
                    if (!invalidChars.Contains(data.Substring(i, 1)))
                    {
                        invalidChars += data[i];
                    }
                }
            }

            // convert the hex characters to a byte array
            byte[] buffer = new byte[data.Length / 2];
            for (int i = 0; i < data.Length; i += 2)
            {
                buffer[i / 2] = byte.Parse(data.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
            }

            // Convert to displayable hex format, with 0x prefix if enabled, and the configured delimiter
            StringBuilder command = new StringBuilder(buffer.Length * (hexPrefix.Length + 2 + delimiter.Length));
            foreach (byte b in buffer)
            {
                if (b == '\r')
                {
                    command.Append(@"\r");
                    command.Append(delimiter);
                }
                else if (b == '\n')
                {
                    command.Append(@"\n");
                    command.Append(delimiter);
                }
                else
                {
                    command.Append(String.Format("{0}{1:X2}{2}", hexPrefix, b, delimiter));
                }
            }

            serialWrite(buffer);
        }

        public int HexToInt(string hexString)
        {
            return int.Parse(hexString,
                System.Globalization.NumberStyles.HexNumber, null);
        }

        public string IntToHex(int number)
        {
            return String.Format("{0:x}", number);
        }

        public string HexToBinary(string HexValue)
        {
            return Convert.ToString(Convert.ToByte(HexValue, 16), 2).PadLeft(8, '0');
        }

        #region Control de Tanques

        #region Functions

        public string[] ManageDataReceivedTanques(string Data)
        {
            try
            {
                
                string[] arrReturn = null;
                if (Data.Length > 0)
                {
                    if (Data.Contains("i201"))
                    {
                        arrReturn = obtenerNivelTanqueDataRecived(Data);
                    }
                    if (Data.Contains("i202"))
                    {
                        arrReturn = obtenerRecepcionTanque_ReceivedData(Data);
                    }
                }
                return arrReturn;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        private string getFechaTiempo_aammdd(string fecha, string Horario)
        {
            try
            {
                string Anio = fecha.Substring(0, 2);
                string Mes = fecha.Substring(2, 2);
                string Dia = fecha.Substring(4, 2);

                string Hora = Horario.Substring(0, 2);
                string Minuto = Horario.Substring(2, 2);

                DateTime dTime = new DateTime(int.Parse(DateTime.Now.Year.ToString().Substring(0, 2) + Anio), int.Parse(Mes), int.Parse(Dia), int.Parse(Hora), int.Parse(Minuto), 0);

                return dTime.ToShortDateString() + " " + dTime.ToShortTimeString();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        private string getIdTanque(int id)
        {
            try
            {
                string sIdTanque = id.ToString();
                if (sIdTanque.Length == 1)
                    sIdTanque += "0" + sIdTanque;
                else
                    sIdTanque += sIdTanque.Substring(sIdTanque.Length - 2, 2);

                return sIdTanque;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        private int BinaryToInt(string binary)
        {
            try
            {
                return Convert.ToInt32(binary, 2);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public double Hex32ToFloat(string hexStr)
        {
            try
            {
                string binStr =
                    HexToBinary(hexStr.Substring(0, 2)) +
                    HexToBinary(hexStr.Substring(2, 2)) +
                    HexToBinary(hexStr.Substring(4, 2)) +
                    HexToBinary(hexStr.Substring(6, 2));
                string S = binStr.Substring(0, 1);
                string E = binStr.Substring(1, 8);
                string M = binStr.Substring(9, 23);

                int nE = BinaryToInt(E);
                int nM = BinaryToInt(M);
                int nDivisor = 8388608;

                double fM = Convert.ToDouble(nM);
                double fDivisor = Convert.ToDouble(nDivisor);

                double exponente = Math.Pow(2, (nE - 127));
                double mantiza = 1.0 + (fM / fDivisor);

                if (S == "1") exponente = exponente * -1;

                return exponente * mantiza;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region Nivel de Tanque

        public bool obtenerNivelTanque(int IdTanque)
        {
            try
            {
                string Command = "\01i201";
                string sIdTanque = getIdTanque(IdTanque);

                SendASCII(Command + sIdTanque);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public string[] obtenerNivelTanqueDataRecived(string Data)
        {
            try
            {
                string[] arrReturn = new string[] { };
                if (!(Data == ""))
                {
                    string ComandoDispensario = Data.Substring(0, 6);
                    string FechaTiempo = getFechaTiempo_aammdd(Data.Substring(6, 6), Data.Substring(12, 4));
                    string idTanque = Data.Substring(16, 2);
                    string idProducto = Data.Substring(18, 1);
                    string status = Data.Substring(19, 4);
                    string RestanteBytes = Data.Substring(23, 2);
                    string VolumenCombustible = Hex32ToFloat(Data.Substring(25, 8)).ToString();
                    string VolumenCombustibleCT = Hex32ToFloat(Data.Substring(33, 8)).ToString();
                    string EspaceEmpy = Hex32ToFloat(Data.Substring(41, 8)).ToString();
                    string AlturaCombustible = Hex32ToFloat(Data.Substring(49, 8)).ToString();
                    string AlturaAgua = Hex32ToFloat(Data.Substring(57, 8)).ToString();
                    string Temperatura = Hex32ToFloat(Data.Substring(65, 8)).ToString();
                    string VolumenAgua = Hex32ToFloat(Data.Substring(73, 8)).ToString();

                    arrReturn =
                        new string[] { 
                            ComandoDispensario, 
                            FechaTiempo,
                            idTanque,
                            idProducto,
                            VolumenCombustible,
                            VolumenCombustibleCT,
                            EspaceEmpy,
                            AlturaCombustible,
                            AlturaAgua,
                            Temperatura,
                            VolumenAgua
                        };
                }
                return arrReturn;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }



        public string[] obtenerNivelTanqueDataRecivedEcco(string Data)
        {
            try
            {
                string[] arrReturn = new string[] { };
                if (!(Data == ""))
                {
                    string ComandoDispensario = Data.Substring(0, 6);
                    string FechaTiempo = getFechaTiempo_aammdd(Data.Substring(6, 6), Data.Substring(12, 4));
                    string idTanque = Data.Substring(16, 2);
                    string idProducto = Data.Substring(18, 1);
                    string status = Data.Substring(19, 4);
                    string RestanteBytes = "07";   //Data.Substring(23, 2);
                    string VolumenCombustible = Hex32ToFloat(Data.Substring(25, 8)).ToString();
                    string VolumenCombustibleCT = Hex32ToFloat(Data.Substring(33, 8)).ToString();
                    string EspaceEmpy = Hex32ToFloat(Data.Substring(41, 8)).ToString();
                    string AlturaCombustible = Hex32ToFloat(Data.Substring(49, 8)).ToString();
                    string AlturaAgua = Hex32ToFloat(Data.Substring(57, 8)).ToString();
                    string Temperatura = Hex32ToFloat(Data.Substring(65, 8)).ToString();
                    string VolumenAgua = Hex32ToFloat(Data.Substring(73, 8)).ToString();

                    arrReturn =
                        new string[] { 
                            ComandoDispensario, 
                            FechaTiempo,
                            idTanque,
                            idProducto,
                            VolumenCombustible,
                            VolumenCombustibleCT,
                            EspaceEmpy,
                            AlturaCombustible,
                            AlturaAgua,
                            Temperatura,
                            VolumenAgua
                        };
                }
                return arrReturn;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region Recepcion de Tanque

        public bool obtenerRecepcionTanque(int IdTanque)
        {
            try
            {
                string Command = "\01i202";
                string sIdTanque = getIdTanque(IdTanque);

                SendASCII(Command + sIdTanque);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public string[] obtenerRecepcionTanque_ReceivedData(string Data)
        {
            try
            {
                string[] arrReturn = new string[] { };
                if (!(Data == ""))
                {
                    string ComandoDispensario = Data.Substring(0, 6);
                    string FechaTiempoActual = getFechaTiempo_aammdd(Data.Substring(6, 6), Data.Substring(12, 4));
                    string idTanque = Data.Substring(16, 2);
                    string idProducto = Data.Substring(18, 1);
                    string deliveryFollow = Data.Substring(19, 2);

                    string fechaTiempoInicio = getFechaTiempo_aammdd(Data.Substring(21, 6), Data.Substring(27, 4));
                    string fechaTiempoFin = getFechaTiempo_aammdd(Data.Substring(31, 6), Data.Substring(37, 4));

                    string RestanteBytes = Data.Substring(41, 2);

                    string VolumenInicio = Hex32ToFloat(Data.Substring(43, 8)).ToString();
                    string VolumenCTInicio = Hex32ToFloat(Data.Substring(51, 8)).ToString();
                    string AlturaAguaInicio = Hex32ToFloat(Data.Substring(59, 8)).ToString();
                    string TemperaturaInicio = Hex32ToFloat(Data.Substring(67, 8)).ToString();

                    string VolumenFin = Hex32ToFloat(Data.Substring(75, 8)).ToString();
                    string VolumenCTFin = Hex32ToFloat(Data.Substring(83, 8)).ToString();
                    string AlturaAguaFin = Hex32ToFloat(Data.Substring(91, 8)).ToString();
                    string TemperaturaFin = Hex32ToFloat(Data.Substring(99, 8)).ToString();

                    string NivelInicio = Hex32ToFloat(Data.Substring(107, 8)).ToString();
                    string NivelFin = Hex32ToFloat(Data.Substring(115, 8)).ToString();

                    arrReturn =
                        new string[] { 
                            ComandoDispensario, 
                            FechaTiempoActual,
                            idTanque,
                            idProducto,

                            fechaTiempoInicio,
                            fechaTiempoFin,
                            
                            VolumenInicio,
                            VolumenCTInicio,
                            AlturaAguaInicio,
                            TemperaturaInicio,
                            NivelInicio,
                            
                            VolumenFin,
                            VolumenCTFin,
                            AlturaAguaFin,
                            TemperaturaFin,
                            NivelFin
                        };
                }
                return arrReturn;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public string[] obtenerRecepcionTanque_ReceivedDataEcco(string Data)
        {
            try
            {
                string[] arrReturn = new string[] { };
                if (!(Data == ""))
                {
                    string ComandoDispensario = Data.Substring(0, 6);
                    string FechaTiempoActual = getFechaTiempo_aammdd(Data.Substring(6, 6), Data.Substring(12, 4));
                    string idTanque = Data.Substring(16, 2);
                    string idProducto = Data.Substring(18, 1);
                    string deliveryFollow = Data.Substring(19, 2);

                    string fechaTiempoInicio = getFechaTiempo_aammdd(Data.Substring(21, 6), Data.Substring(27, 4));
                    string fechaTiempoFin = getFechaTiempo_aammdd(Data.Substring(31, 6), Data.Substring(37, 4));

                    string RestanteBytes = "0A"; //Data.Substring(41, 2);

                    string VolumenInicio = Hex32ToFloat(Data.Substring(43, 8)).ToString();
                    string VolumenCTInicio = Hex32ToFloat(Data.Substring(51, 8)).ToString();
                    string AlturaAguaInicio = Hex32ToFloat(Data.Substring(59, 8)).ToString();
                    string TemperaturaInicio = Hex32ToFloat(Data.Substring(67, 8)).ToString();

                    string VolumenFin = Hex32ToFloat(Data.Substring(75, 8)).ToString();
                    string VolumenCTFin = Hex32ToFloat(Data.Substring(83, 8)).ToString();
                    string AlturaAguaFin = Hex32ToFloat(Data.Substring(91, 8)).ToString();
                    string TemperaturaFin = Hex32ToFloat(Data.Substring(99, 8)).ToString();

                    string NivelInicio = Hex32ToFloat(Data.Substring(107, 8)).ToString();
                    string NivelFin = Hex32ToFloat(Data.Substring(115, 8)).ToString();

                    arrReturn =
                        new string[] { 
                            ComandoDispensario, 
                            FechaTiempoActual,
                            idTanque,
                            idProducto,

                            fechaTiempoInicio,
                            fechaTiempoFin,
                            
                            VolumenInicio,
                            VolumenCTInicio,
                            AlturaAguaInicio,
                            TemperaturaInicio,
                            NivelInicio,
                            
                            VolumenFin,
                            VolumenCTFin,
                            AlturaAguaFin,
                            TemperaturaFin,
                            NivelFin
                        };
                }
                return arrReturn;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        #endregion

        #endregion
    }
}
